package org.je.dev.image;

import java.awt.image.BufferedImage;

/**
 * 一种可以快速计算的Image对象
 * 
 * @author fanyifeng
 */
public class FastImage
{

	public static final int	alphaMin	= 11;
	public static final int	alphaMax	= 244;

	private final int		_w;
	private final int		_h;
	private byte[]			alphas;
	private byte[]			rgbs;

	/**
	 * @param w
	 * @param h
	 * @param useAlpha
	 */
	public FastImage(int w, int h) {
		this._w = w;
		this._h = h;
		this.alphas = new byte[w * h];
		this.rgbs = new byte[w * h * 3];
	}

	public int w() {
		return _w;
	}

	public int h() {
		return _h;
	}

	public byte[] alphas() {
		return alphas;
	}

	public byte[] rgbs() {
		return rgbs;
	}

	/**
	 * @param img
	 * @param x
	 * @param y
	 * @param useAlpha
	 */
	public void drawImage(FastImage img, int x, int y, boolean useAlpha) //
	{
		if (x >= _w || y >= _h || x + img.w() < 0 || y + img.h() < 0)
			return;

		int[] cis = canvasOff_imgOff_drawSize(x ,img._w ,this._w);
		int canvasX = cis[0];
		int imgX = cis[1];
		int dw = cis[2];

		cis = canvasOff_imgOff_drawSize(y ,img._h ,this._h);
		int canvasY = cis[0];
		int imgY = cis[1];
		int dh = cis[2];

		byte[] imgRgbs = img.rgbs, imgAlp = img.alphas;
		int canvasOff, imgOff;

		if (useAlpha == false) {
			for (int dRow = 0; dRow < dh; ++dRow) {
				canvasOff = ( (canvasY + dRow) * _w + canvasX) * 3;
				imgOff = ( (imgY + dRow) * img._w + imgX) * 3;
				System.arraycopy(imgRgbs ,imgOff ,rgbs ,canvasOff ,3 * dw);
			}
			return;// 记得返回
		}

		int imgAlphaOff, temAf;

		for (int dRow = 0; dRow < dh; ++dRow) {
			canvasOff = ( (canvasY + dRow) * _w + canvasX) * 3;
			imgAlphaOff = (imgY + dRow) * img._w + imgX;
			imgOff = imgAlphaOff * 3;

			for (int i = 0; i < dw; ++i, ++imgAlphaOff) {
				temAf = imgAlp[imgAlphaOff] & 0xff;
				if (temAf < alphaMin) {
					canvasOff += 3;
					imgOff += 3;
					continue;
				}
				if (temAf > alphaMax) {
					rgbs[canvasOff++] = imgRgbs[imgOff++];
					rgbs[canvasOff++] = imgRgbs[imgOff++];
					rgbs[canvasOff++] = imgRgbs[imgOff++];
					continue;
				}
				rgbs[canvasOff] = mix(rgbs[canvasOff] & 0xff ,imgRgbs[imgOff] & 0xff ,
						temAf);
				++canvasOff;
				++imgOff;
				rgbs[canvasOff] = mix(rgbs[canvasOff] & 0xff ,imgRgbs[imgOff] & 0xff ,
						temAf);
				++canvasOff;
				++imgOff;
				rgbs[canvasOff] = mix(rgbs[canvasOff] & 0xff ,imgRgbs[imgOff] & 0xff ,
						temAf);
				++canvasOff;
				++imgOff;
			}
		}
	}

	/**
	 * @param img
	 * @param x
	 * @param y
	 * @param ctrlShorts
	 * @param ctrlMark
	 * @param ctrlAlpha
	 */
	public void complexDraw(FastImage img, int x, int y, short[] ctrlShorts,
			short ctrlMark, boolean ctrlAlpha) {
		if (x >= _w || y >= _h || x + img.w() < 0 || y + img.h() < 0)
			return;

		int[] cis = canvasOff_imgOff_drawSize(x ,img._w ,this._w);
		int canvasX = cis[0];
		int imgX = cis[1];
		int dw = cis[2];

		cis = canvasOff_imgOff_drawSize(y ,img._h ,this._h);
		int canvasY = cis[0];
		int imgY = cis[1];
		int dh = cis[2];

		byte[] imgRgbs = img.rgbs, imgAlp = img.alphas;
		int canvasOff, ctrlOff, imgOff, imgAlphaOff, temAf;

		short pCtrl;

		for (int dRow = 0; dRow < dh; ++dRow) //
		{
			ctrlOff = (canvasY + dRow) * _w + canvasX;
			canvasOff = ctrlOff * 3;
			imgAlphaOff = (imgY + dRow) * img._w + imgX;
			imgOff = imgAlphaOff * 3;

			for (int i = 0; i < dw; ++i, ++imgAlphaOff, ++ctrlOff) //
			{
				temAf = imgAlp[imgAlphaOff] & 0xff;
				if (temAf < alphaMin) {
					canvasOff += 3;
					imgOff += 3;
					continue;// 这种情形直接返回
				}
				pCtrl = ctrlShorts[ctrlOff];
				if (ctrlAlpha == false || pCtrl == 0 || pCtrl == ctrlMark) {
					ctrlShorts[ctrlOff] = ctrlMark;
					rgbs[canvasOff] = imgRgbs[imgOff];
					++canvasOff;
					++imgOff;
					rgbs[canvasOff] = imgRgbs[imgOff];
					++canvasOff;
					++imgOff;
					rgbs[canvasOff] = imgRgbs[imgOff];
					++canvasOff;
					++imgOff;
					continue;
				}
				rgbs[canvasOff] = mix(rgbs[canvasOff] & 0xff ,imgRgbs[imgOff] & 0xff ,
						temAf);
				++canvasOff;
				++imgOff;
				rgbs[canvasOff] = mix(rgbs[canvasOff] & 0xff ,imgRgbs[imgOff] & 0xff ,
						temAf);
				++canvasOff;
				++imgOff;
				rgbs[canvasOff] = mix(rgbs[canvasOff] & 0xff ,imgRgbs[imgOff] & 0xff ,
						temAf);
				++canvasOff;
				++imgOff;
			}
		}
	}

	/**
	 * @param img
	 * @param x
	 * @param y
	 * @param ctrlShorts
	 */
	public void drawByCtrls(FastImage img, int x, int y, short[] ctrlShorts) {
		if (x >= _w || y >= _h || x + img.w() < 0 || y + img.h() < 0)
			return;

		int[] cis = canvasOff_imgOff_drawSize(x ,img._w ,this._w);
		int canvasX = cis[0];
		int imgX = cis[1];
		int dw = cis[2];

		cis = canvasOff_imgOff_drawSize(y ,img._h ,this._h);
		int canvasY = cis[0];
		int imgY = cis[1];
		int dh = cis[2];

		byte[] imgRgbs = img.rgbs, imgAlp = img.alphas;
		int canvasOff, ctrlOff, imgOff, imgAlphaOff, temAf;

		for (int dRow = 0; dRow < dh; ++dRow) {
			ctrlOff = (canvasY + dRow) * _w + canvasX;
			canvasOff = ctrlOff * 3;
			imgAlphaOff = (imgY + dRow) * img._w + imgX;
			imgOff = imgAlphaOff * 3;

			for (int i = 0; i < dw; ++i, ++imgAlphaOff, ++ctrlOff) {
				temAf = imgAlp[imgAlphaOff] & 0xff;
				if (temAf < alphaMin) {
					canvasOff += 3;
					imgOff += 3;
					continue;// 这种情形直接返回
				}

				if (ctrlShorts[ctrlOff] == 0) {
					rgbs[canvasOff] = imgRgbs[imgOff];
					++canvasOff;
					++imgOff;
					rgbs[canvasOff] = imgRgbs[imgOff];
					++canvasOff;
					++imgOff;
					rgbs[canvasOff] = imgRgbs[imgOff];
					++canvasOff;
					++imgOff;
					continue;
				}
				rgbs[canvasOff] = (byte) ( ( (rgbs[canvasOff] & 0xff) * 3 + (imgRgbs[imgOff] & 0xff)) >> 2);
				++canvasOff;
				++imgOff;
				rgbs[canvasOff] = (byte) ( ( (rgbs[canvasOff] & 0xff) * 3 + (imgRgbs[imgOff] & 0xff)) >> 2);
				++canvasOff;
				++imgOff;
				rgbs[canvasOff] = (byte) ( ( (rgbs[canvasOff] & 0xff) * 3 + (imgRgbs[imgOff] & 0xff)) >> 2);
				++canvasOff;
				++imgOff;
			}
		}

	}

	public void drawByPY(FastImage img, int x, int y, short[] ctrlShorts, short py) {
		if (x >= _w || y >= _h || x + img.w() < 0 || y + img.h() < 0)
			return;

		int[] cis = canvasOff_imgOff_drawSize(x ,img._w ,this._w);
		int canvasX = cis[0];
		int imgX = cis[1];
		int dw = cis[2];

		cis = canvasOff_imgOff_drawSize(y ,img._h ,this._h);
		int canvasY = cis[0];
		int imgY = cis[1];
		int dh = cis[2];

		byte[] imgRgbs = img.rgbs, imgAlp = img.alphas;
		int canvasOff, ctrlOff, imgOff, imgAlphaOff, temAf;

		short ctl;

		for (int dRow = 0; dRow < dh; ++dRow) {
			ctrlOff = (canvasY + dRow) * _w + canvasX;
			canvasOff = ctrlOff * 3;
			imgAlphaOff = (imgY + dRow) * img._w + imgX;
			imgOff = imgAlphaOff * 3;

			for (int i = 0; i < dw; ++i, ++imgAlphaOff, ++ctrlOff) {
				temAf = imgAlp[imgAlphaOff] & 0xff;
				if (temAf < alphaMin) {
					canvasOff += 3;
					imgOff += 3;
					continue;// 这种情形直接返回
				}

				ctl = ctrlShorts[ctrlOff];
				if (ctl == 0) {
					rgbs[canvasOff] = imgRgbs[imgOff];
					++canvasOff;
					++imgOff;
					rgbs[canvasOff] = imgRgbs[imgOff];
					++canvasOff;
					++imgOff;
					rgbs[canvasOff] = imgRgbs[imgOff];
					++canvasOff;
					++imgOff;
					continue;
				}

				ctl = (short) (ctl & 0xff);

				if (py > (ctl & 0xff)) {
					rgbs[canvasOff] = (byte) ( ( (rgbs[canvasOff] & 0xff) + (imgRgbs[imgOff] & 0xff)) >> 1);
					++canvasOff;
					++imgOff;
					rgbs[canvasOff] = (byte) ( ( (rgbs[canvasOff] & 0xff) + (imgRgbs[imgOff] & 0xff)) >> 1);
					++canvasOff;
					++imgOff;
					rgbs[canvasOff] = (byte) ( ( (rgbs[canvasOff] & 0xff) + (imgRgbs[imgOff] & 0xff)) >> 1);
					++canvasOff;
					++imgOff;
				} else {
					canvasOff += 3;
					imgOff += 3;
				}

			}
		}
	}

	private byte mix(int canvas, int img, int alpha) {
		return (byte) ( (canvas * (255 - alpha) + img * alpha) / 255);
	}

	/**
	 * @param color
	 */
	public void clear(int color) {
		byte r = (byte) (color >> 16);
		byte g = (byte) (color >> 8);
		byte b = (byte)color;
		int len = rgbs.length;
		for (int i = 0; i < len;) {
			rgbs[i] = r;
			++i;
			rgbs[i] = g;
			++i;
			rgbs[i] = b;
			++i;
		}
	}

	public void clearRgbs(byte[] rgbArr) {
		System.arraycopy(rgbArr ,0 ,rgbs ,0 ,rgbArr.length);
	}

	/**
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 * @param color
	 */
	public void fillRect(int x, int y, int w, int h, int color) {
		drawRect(x ,y ,w ,h ,color ,true);
	}

	/**
	 * @param x
	 * @param y
	 * @param w
	 * @param h
	 * @param color
	 */
	public void drawRect(int x, int y, int w, int h, int color) {
		drawRect(x ,y ,w ,h ,color ,false);
	}

	private void drawRect(int x, int y, int w, int h, int color, boolean isFill) {
		if (x >= _w || y >= _h || w <= 0 || h <= 0 || x + w < 0 || y + h < 0)
			return;
		byte r = (byte) (color >> 16);
		byte g = (byte) (color >> 8);
		byte b = (byte)color;

		int xs = x < 0 ? 0 : x;
		int xe = x + w;
		xe = xe > _w ? _w : xe;

		int ys = y < 0 ? 0 : y;
		int ye = y + h;
		ye = ye > _h ? _h : ye;
		int off = 0;
		if (isFill) {
			for (int iy = ys; iy < ye; ++iy) {
				for (int ix = xs; ix < xe; ++ix) {
					off = (iy * _w + ix) * 3;
					rgbs[off] = r;
					++off;
					rgbs[off] = g;
					++off;
					rgbs[off] = b;
				}
			}
		} else {
			int[] yArr = { ys, ye - 1 };

			for (int iy : yArr) {
				if (iy < 0) {
					continue;
				}
				for (int ix = xs; ix < xe; ++ix) {
					off = (iy * _w + ix) * 3;
					rgbs[off] = r;
					++off;
					rgbs[off] = g;
					++off;
					rgbs[off] = b;
				}
			}
			int[] xArr = { xs, xe - 1 };
			for (int ix : xArr) {
				if (ix < 0) {
					continue;
				}
				for (int iy = ys; iy < ye; ++iy) {
					off = (iy * _w + ix) * 3;
					rgbs[off] = r;
					++off;
					rgbs[off] = g;
					++off;
					rgbs[off] = b;
				}
			}
		}
	}

	void setARGB(int x, int y, int argb) {
		int ix = y * _w + x;
		alphas[ix] = (byte) (argb >>> 24);
		ix *= 3;
		rgbs[ix] = (byte) (argb >> 16);
		++ix;
		rgbs[ix] = (byte) (argb >> 8);
		++ix;
		rgbs[ix] = (byte)argb;
	}

	/**
	 * @param alpha
	 * @param clearMin
	 */
	public void clearAlpha(byte alpha, boolean clearMin) {
		int len = alphas.length;
		if (clearMin) {
			for (int i = 0; i < len; ++i) {
				alphas[i] = alpha;
			}
		} else {
			for (int i = 0; i < len; ++i) {
				if ( (alphas[i] & 0xff) > alphaMin) {
					alphas[i] = alpha;
				}
			}
		}
	}

	public BufferedImage toBufferedImage() {
		BufferedImage img = new BufferedImage(_w,_h,BufferedImage.TYPE_INT_ARGB);
		int i = 0, rgbIx = 0;
		for (; i < rgbs.length; ++rgbIx) {
			int rgb = 0;
			rgb |= (alphas[rgbIx] & 0xff) << 24;

			rgb |= (rgbs[i] & 0xff) << 16;
			++i;
			rgb |= (rgbs[i] & 0xff) << 8;
			++i;
			rgb |= (rgbs[i] & 0xff);
			++i;
			img.setRGB(rgbIx % _w ,rgbIx / _w ,rgb);
		}
		return img;
	}

	private byte[]	rgbas	= null;

	public byte[] toRGBAs() {
		if (rgbas == null) {
			rgbas = new byte[alphas.length * 4];
		}
		int pRgb = 0, pAlp = 0, off = 0;
		int end = rgbs.length;

		while( pRgb < end ) {
			System.arraycopy(rgbs ,pRgb ,rgbas ,off ,3);
			rgbas[off + 3] = alphas[pAlp];
			++pAlp;
			pRgb += 3;
			off += 4;
		}
		return rgbas;
	}

	private int[] canvasOff_imgOff_drawSize(int canvasOff, int imgSize, int canvasSize) {
		int imgOff, drawSize;
		if (canvasOff < 0) {
			imgOff = -canvasOff;
			canvasOff = 0;
			drawSize = imgSize - imgOff;
			if (drawSize > canvasSize)
				drawSize = canvasSize;
		} else {
			imgOff = 0;
			drawSize = imgSize;
			if (drawSize + canvasOff > canvasSize)
				drawSize = canvasSize - canvasOff;
		}
		return new int[] { canvasOff, imgOff, drawSize };
	}

	public void deleteAlphas() {
		this.alphas = null;
	}
}
